package com.tengju.data.domain.model.report;


import com.tengju.data.domain.model.template.SubsidyTemplate;
import com.tengju.data.domain.model.user.*;
import lombok.Data;
import lombok.Getter;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.tengju.data.domain.model.report.AchievementBonusCalConfig.ACHIEVEMENT_BONUS_THRESHOLD;

@Getter
public class MarketAchievement {

    private Map<Integer, Bonus> typeBonusMap;

    private Map<Integer,Achievement> typeAchievementMap;

    private Integer month;

    public MarketAchievement(Integer month) {
        this.month = month;
        this.typeBonusMap = new HashMap<>();
        this.typeAchievementMap = new HashMap<>();
    }

    /**
     * 合并下级大团队业绩
     * @param childMarketAchievement
     */
    public void combineChildAchievement(MarketAchievement childMarketAchievement) {
        Map<Integer, Achievement> childTypeAchievementMap = childMarketAchievement.getTypeAchievementMap();
        if (CollectionUtils.isEmpty(childTypeAchievementMap)) {
            return;
        }
        //下级的出货额如果小于0，不累计到上级
        childTypeAchievementMap
                .values().stream()
                .forEach(this::doCombineChildAchievement);
    }

    private void doCombineChildAchievement(Achievement childAchievement) {
        if (childAchievement == null) {
            return;
        }
        Integer teamRatioType = childAchievement.getTeamRatioType();
        Achievement achievement = typeAchievementMap.getOrDefault(teamRatioType, new Achievement(teamRatioType));
        achievement.combineChildAchievement(childAchievement);
        typeAchievementMap.putIfAbsent(teamRatioType,achievement);
    }

    /**
     * 合并自身小团队业绩
     * @param teamRatioType
     * @param selfMoneyOut
     * @param smallTeamMoneyOut
     * @param smallTeamCancelMoneyOut
     * @param smallTeamCancelBeforeMoneyOut
     */
    public void addSelfTeamAchievement(Integer teamRatioType, BigDecimal selfMoneyOut, BigDecimal smallTeamMoneyOut, BigDecimal smallTeamCancelMoneyOut, BigDecimal smallTeamCancelBeforeMoneyOut) {
        Achievement achievement = typeAchievementMap.getOrDefault(teamRatioType, new Achievement(teamRatioType));
        achievement.setMarketIndex(achievement.getMarketIndex().add(smallTeamMoneyOut));
        achievement.setSmallTeamCancelAchievement(achievement.getSmallTeamCancelAchievement().add(smallTeamCancelBeforeMoneyOut));
        achievement.setTheoryAchievement(achievement.getTheoryAchievement().add(smallTeamMoneyOut).add(smallTeamCancelBeforeMoneyOut));
        achievement.setAllTeamCancelAchievement(achievement.getAllTeamCancelAchievement().add(smallTeamCancelBeforeMoneyOut));
        achievement.setSettleAchievement(smallTeamMoneyOut);
        typeAchievementMap.putIfAbsent(teamRatioType,achievement);
    }

    /**
     * 计算市场补贴
     */
    public void doCalBonus() {
        if (CollectionUtils.isEmpty(typeBonusMap)) {
            return;
        }
        for (Integer teamRatioType : typeAchievementMap.keySet()) {
            Achievement achievement = typeAchievementMap.get(teamRatioType);
            double factor = 0;
            Bonus bonus = typeBonusMap.get(teamRatioType);
            factor = bonus.getFactor();
            BigDecimal totalBonus = achievement.getSettleAchievement().multiply(BigDecimal.valueOf(factor));
            // 如果收益计算小于0，置0
            if (totalBonus.compareTo(BigDecimal.ZERO) < 0) {
                totalBonus = BigDecimal.ZERO;
            }
            bonus.setBonus(totalBonus,AchievementBonusStatusEnum.WAIT_SETTLE);
            typeBonusMap.put(teamRatioType,bonus);
        }
    }

    /**
     * 冻结市场补贴
     */
    public void freezeBonus() {
        if (CollectionUtils.isEmpty(typeBonusMap)) {
            return;
        }
        typeBonusMap.values().stream()
                .forEach(Bonus::freeze);
    }

    public List<AchievementBonus> printBonus(UserLevelInfo user,
                                             Map<Integer, SubsidyTemplate> subsidyTemplateMap,
                                             UserStarFeature starFeature,
                                             int version) {
        List<AchievementBonus> achievementBonusList = new ArrayList<>();
        for (Integer teamRatioType : typeAchievementMap.keySet()) {
            if (!subsidyTemplateMap.containsKey(teamRatioType)) {
                continue;
            }
            Achievement achievement = typeAchievementMap.get(teamRatioType);
            Bonus bonus = typeBonusMap.get(teamRatioType);
            AchievementBonus achievementBonus = new AchievementBonus(
                    user,
                    month,
                    achievement,
                    bonus,
                    starFeature,
                    version);
            achievementBonusList.add(achievementBonus);
        }
        return achievementBonusList;
    }

    /**
     * 判断用户没有市场补贴资格的情况
     * 1. 市场指数没有达到阈值
     * @param user
     * @param marketIndex
     * @return
     */
    private boolean qualifyMarketBonus(UserLevelInfo user, BigDecimal marketIndex) {
        if (user.getLevel() != UserLevelEnum.BLACK) {
            return false;
        }
        return marketIndex.compareTo(ACHIEVEMENT_BONUS_THRESHOLD) >= 0;
    }

    /**
     * 确定商品补贴系数
     * 1. 星级等级
     * 2. 是否特批名单
     * 3. 出货额范围
     * @param subsidyTemplateMap
     * @param leaderUserInfo
     * @param
     */
    public void calTypeDebate(Map<Integer, SubsidyTemplate> subsidyTemplateMap, UserLevelInfo leaderUserInfo, UserStarFeature starFeature) {
        Double bonusDebateLimit = AchievementBonusCalConfig.getStarBonusDebateLimit(starFeature.getStarLevel() == StarLevelEnum.NONE ? starFeature.getInviteStarLevel() : starFeature.getStarLevel());
        if (starFeature.isMeetTag(UserStarFeature.STAR_SPECIAL_USER_TAG)) {
            bonusDebateLimit = Math.max(AchievementBonusCalConfig.getSpecialStarBonusDebateLimit(month),bonusDebateLimit);
        }
        BigDecimal totalMoneyOut = typeAchievementMap.values().stream()
                .map(Achievement::getMarketIndex)
                .reduce(BigDecimal::add)
                .orElse(BigDecimal.ZERO);
        for (Integer teamRatioType : typeAchievementMap.keySet()) {
            SubsidyTemplate subsidyTemplate = subsidyTemplateMap.get(teamRatioType);
            double factor = 0;
            if (subsidyTemplate != null && qualifyMarketBonus(leaderUserInfo,totalMoneyOut)) {
                factor = subsidyTemplate.getDebate(totalMoneyOut);
            }
            factor = Math.max(Math.min(factor, bonusDebateLimit), 0D);
            Bonus profitData = new Bonus(teamRatioType,factor,month);
            typeBonusMap.put(teamRatioType,profitData);
        }
    }

    public double getBonusDebate(Integer teamRatioType) {
        if (!typeBonusMap.containsKey(teamRatioType)) {
            return 0D;
        }
        return typeBonusMap.get(teamRatioType).getFactor();
    }

    public void setSettleParent(Integer teamRatioType, UserLevelInfo settleParentLeaderUserInfo) {
        if (!typeBonusMap.containsKey(teamRatioType)) {
            return;
        }
        typeBonusMap.get(teamRatioType).setSettleParent(settleParentLeaderUserInfo);
    }

    public void combineSettleAchievementAndBonus(Integer teamRatioType, MarketAchievement otherMarketAchievement) {
        Achievement achievement = typeAchievementMap.getOrDefault(teamRatioType, new Achievement(teamRatioType));
        Map<Integer, Achievement> otherTypeAchievementMap = otherMarketAchievement.getTypeAchievementMap();
        Map<Integer, Bonus> otherTypeBonusMap = otherMarketAchievement.getTypeBonusMap();
        Achievement otherAchievement = otherTypeAchievementMap.get(teamRatioType);
        Bonus otherBonus = otherTypeBonusMap.get(teamRatioType);
        Bonus bonus = typeBonusMap.get(teamRatioType);
        achievement.combineSettleAchievement(otherAchievement);
        bonus.combineSettleBonus(otherBonus);
        this.typeAchievementMap.putIfAbsent(teamRatioType,achievement);
    }

    @Getter
    static class Bonus {
        private Integer teamRatioType;

        private Double factor;

        /**
         * 大团队leader补贴
         */
        private BigDecimal leaderBonus  = BigDecimal.ZERO;

        /**
         * 大团队整体补贴
         */
        private BigDecimal totalMarketBonus =  BigDecimal.ZERO;

        private Integer month;

        private UserIdCode settlePIdCode;

        private UserId settlePUserId;

        private AchievementBonusStatusEnum status;

        private BigDecimal settleTeamBonus = BigDecimal.ZERO;

        public Bonus(Integer teamRatioType, double factor, Integer month) {
            this.teamRatioType = teamRatioType;
            this.factor = factor;
            this.month = month;
        }

        public void freeze() {
            this.status = AchievementBonusStatusEnum.FREEZE;
        }

        public void combineSettleBonus(Bonus otherBonus) {
            this.settleTeamBonus = this.settleTeamBonus.add(otherBonus.getTotalMarketBonus());
        }

        public void setBonus(BigDecimal totalBonus, AchievementBonusStatusEnum status) {
            this.status = status;
            this.totalMarketBonus = totalBonus;
            this.leaderBonus = this.totalMarketBonus.subtract(this.settleTeamBonus);
        }

        public void setSettleParent(UserLevelInfo settleParentLeaderUserInfo) {
            this.settlePIdCode = settleParentLeaderUserInfo.getIdCode();
            this.settlePUserId = settleParentLeaderUserInfo.getUserId();
        }
    }

    @Data
    static class Achievement {
        private Integer teamRatioType;

        /**
         * 小团队取消本月之前业绩（出货额）
         */
        private BigDecimal smallTeamCancelAchievement = BigDecimal.ZERO;

        /**
         * 大团队取消本月之前业绩（出货额）
         */
        private BigDecimal allTeamCancelAchievement = BigDecimal.ZERO;

        /**
         * 大团队本月有效下单业绩（取消之前订单业绩不扣除）
         */
        private BigDecimal theoryAchievement = BigDecimal.ZERO;

        /**
         * 大团队有效业绩（出货额）(市场指数)
         */
        private BigDecimal marketIndex = BigDecimal.ZERO;

        /**
         * 结算团队业绩
         */
        private BigDecimal settleAchievement = BigDecimal.ZERO;

        public Achievement(Integer teamRatioType) {
            this.teamRatioType = teamRatioType;
        }

        public Achievement combineChildAchievement(Achievement other) {
            this.allTeamCancelAchievement = this.allTeamCancelAchievement.add(other.getAllTeamCancelAchievement());
            this.theoryAchievement = this.theoryAchievement.add(other.getTheoryAchievement());
            this.marketIndex = this.marketIndex.add(other.getMarketIndex());
            return this;
        }

        public void combineSettleAchievement(Achievement other) {
            this.settleAchievement = this.settleAchievement.add(other.getSettleAchievement());
        }
    }

}
